#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/cdev.h>

#include <linux/sched.h>  /* current and everything */
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h>     /* everything... */
#include <linux/types.h>  /* size_t */
#include <linux/completion.h>

struct cdev *singleUID_cdev;	

static int singleUID_major = 0;
static int singleUID_minor = 0;

module_param(singleUID_major, int, S_IRUGO);
module_param(singleUID_minor, int, S_IRUGO);


DECLARE_COMPLETION(comp);


/************************************************************************
 *
 * Next, the "uid" device. It can be opened multiple times by the
 * same user, but access is denied to other users if the device is open
 */

static int singleUID_count;	/* initialized to 0 by default */
static uid_t singleUID_owner;	/* initialized to 0 by default */
static spinlock_t singleUID_lock = SPIN_LOCK_UNLOCKED;

static int singleUID_open(struct inode *inode, struct file *filp)
{
//	struct cdev *dev = &singleUID_cdev; /* device information */

	spin_lock(&singleUID_lock);
	if (singleUID_count && 
			(singleUID_owner != current->uid) &&  /* allow user */
			(singleUID_owner != current->euid) && /* allow whoever did su */
			!capable(CAP_DAC_OVERRIDE)) { /* still allow root */
		spin_unlock(&singleUID_lock);
		return -EBUSY;   /* -EPERM would confuse the user */
	}

	if (singleUID_count == 0)
		singleUID_owner = current->uid; /* grab it */

	singleUID_count++;
	spin_unlock(&singleUID_lock);

	return 0;          /* success */
}

static int singleUID_release(struct inode *inode, struct file *filp)
{
	spin_lock(&singleUID_lock);
	singleUID_count--; /* nothing else */
	spin_unlock(&singleUID_lock);
	return 0;
}


ssize_t singleUID_read (struct file *filp, char __user *buf, size_t count, loff_t *pos)
{
	printk(KERN_DEBUG "process %i (%s) going to sleep\n",
			current->pid, current->comm);
	wait_for_completion(&comp);
	printk(KERN_DEBUG "awoken %i (%s)\n", current->pid, current->comm);
	return 0; /* EOF */
}

ssize_t singleUID_write (struct file *filp, const char __user *buf, size_t count,
		loff_t *pos)
{
	printk(KERN_DEBUG "process %i (%s) awakening the readers...\n",
			current->pid, current->comm);
	complete(&comp);
	return count; /* succeed, to avoid retrial */
}


struct file_operations singleUID_fops = {
	.owner = THIS_MODULE,
	.read =  singleUID_read,
	.write = singleUID_write,
	.open =  singleUID_open,
	.release =   singleUID_release,
};


void singleUID_cleanup(void)
{

#if 0
	unregister_chrdev(complete_major, "complete");
#endif

//#if 0
	dev_t devno = MKDEV(singleUID_major, singleUID_minor);
	/* Get rid of our char dev entries */
	if (singleUID_cdev) {
		cdev_del(singleUID_cdev);		
		kfree(singleUID_cdev);
	}
	/* cleanup_module is never called if registering failed */
	unregister_chrdev_region(devno,1);
//#endif

}


int singleUID_init(void)
{
#if 0
	int result;

	/*
	 * Register your major, and accept a dynamic number
	 */
	result = register_chrdev(complete_major, "complete", &complete_fops);
	if (result < 0)
		return result;
	if (complete_major == 0)
		complete_major = result; /* dynamic */
	return 0;
#endif

//#if 0
	int result,err;
	dev_t dev = 0;

	if (singleUID_major) {
		dev = MKDEV(singleUID_major, singleUID_minor);
		result = register_chrdev_region(dev, 1 , "singleUID");
	} else {
		result = alloc_chrdev_region(&dev, singleUID_minor , 1,
				"singleUID");
		singleUID_major = MAJOR(dev);
	}
	if (result < 0) {
		printk(KERN_WARNING "singleUID: can't get major %d\n", singleUID_major);
		return result;
	}

        /* 
	 * allocate the devices -- we can't have them static, as the number
	 * can be specified at load time
	 */

	//complete_cdev = cdev_alloc();	//cdev_alloc() and kmalloc()  are both ok!  use them as you want!
	singleUID_cdev = kmalloc( sizeof(struct cdev), GFP_KERNEL);

	if (!singleUID_cdev) {
		result = -ENOMEM;
		goto fail;  /* Make this more graceful */
	}
	
	cdev_init(singleUID_cdev, &singleUID_fops);

	singleUID_cdev->owner = THIS_MODULE;

	err = cdev_add (singleUID_cdev, dev, 1);
	/* Fail gracefully if need be */
	if (err)
		printk(KERN_NOTICE "Error %d adding singleUID_cdev", err);

	return 0; /* succeed */

  fail:
	singleUID_cleanup();
	return result;
//#endif
}

module_init(singleUID_init);
module_exit(singleUID_cleanup);

MODULE_AUTHOR("Tekkaman Ninja");
MODULE_LICENSE("Dual BSD/GPL");
